package advanced;

import JaCoP.constraints.IfThen;
import JaCoP.constraints.Sum;
import JaCoP.constraints.XeqY;
import JaCoP.constraints.XneqY;
import JaCoP.core.IntVar;
import JaCoP.core.Store;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.MostConstrainedStatic;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleMatrixSelect;

public class Rostering {
	private static Store store = new Store();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		long T1, T2, T;
		T1 = System.currentTimeMillis();
		scheduleWorkers();
		T2 = System.currentTimeMillis();
		T = T2 - T1;
		System.out.println("\n\t*** Execution time = " + T + " ms");
	}

	private static void scheduleWorkers() {
		/*
		 * Following matrix has workers in rows and weekdays in columns, so
		 * workers of type A has y = {0 .. 3}, B has y = {4 .. 6} and C has y =
		 * {7 .. 8} The week starts with Monday being x = 0
		 */
		IntVar[][] matrix = new IntVar[9][7];
		IntVar[][] transMatrix = new IntVar[7][9];

		for (int day = 0; day < 7; day++) {
			for (int worker = 0; worker < 9; worker++) {
				IntVar var = new IntVar(store, "Worker: " + worker + ", day: "
						+ day, 0, 1);
				matrix[worker][day] = var;
				transMatrix[day][worker] = var;

			}
		}

		for (int A = 0; A < 4; A++) {
			workDaysAWeek(matrix[A], 5);
			maxDaysInARow(matrix[A], 3);
		}
		for (int B = 4; B < 7; B++) {
			workDaysAWeek(matrix[B], 4);
			maxDaysInARow(matrix[B], 2);
		}
		for (int C = 7; C < 9; C++) {
			workDaysAWeek(matrix[C], 5);
			maxDaysInARow(matrix[C], 3);
		}

		for (int day = 0; day < 7; day++) {
			workersADay(transMatrix[day]);
			workingTogether(transMatrix[day]);
		}
		
		
		Search<IntVar> label = new DepthFirstSearch<IntVar>();

		SelectChoicePoint<IntVar> select = new SimpleMatrixSelect<IntVar>(
				matrix, new MostConstrainedStatic<IntVar>(), new IndomainMin<IntVar>());
		label.getSolutionListener().searchAll(true);
		

		boolean Result = label.labeling(store, select);
		
		if (Result) {
			System.out.println("\tM\tTu\tW\tTh\tF\tSa\tSu");
			for (int aWorker = 0; aWorker < 4; aWorker++) {
				System.out.print("A" + (aWorker + 1) + "\t");
				for (int day = 0; day < 7; day++) {
					System.out.print(matrix[aWorker][day].value() + "\t");
				}
				System.out.println();
			}
			System.out.println();
			for (int bWorker = 4; bWorker < 7; bWorker++) {
				System.out.print("B" + (bWorker - 3) + "\t");
				for (int day = 0; day < 7; day++) {
					System.out.print(matrix[bWorker][day].value() + "\t");
				}
				System.out.println();
			}
			System.out.println();
			for (int cWorker = 7; cWorker < 9; cWorker++) {
				System.out.print("C" + (cWorker - 6) + "\t");
				for (int day = 0; day < 7; day++) {
					System.out.print(matrix[cWorker][day].value() + "\t");
				}
				System.out.println();
			}

		} else {
			System.out.println("\n*** No");
		}

	}

	private static void maxDaysInARow(IntVar[] workerWeek, int max) {
		for (int i = 0; i < 7; i++) {
			IntVar[] group = new IntVar[max + 1];
			for (int j = 0; j < max + 1; j++) {
				group[j] = workerWeek[(i + j) % 7];
			}
			IntVar sum = new IntVar(store, "", 0, max);
			store.impose(new Sum(group, sum));
		}

	}

	private static void workersADay(IntVar[] workers) {
		store.impose(new Sum(workers, new IntVar(store, "", 6, 6))); // can
																		// never
																		// be
																		// more
																		// than
																		// 6
																		// workers
																		// a day
	}

	private static void workingTogether(IntVar[] workers) {
		IntVar[] aS = { workers[0], workers[1], workers[2], workers[3] };
		IntVar[] bS = { workers[4], workers[5], workers[6] };
		IntVar[] cS = { workers[7], workers[8] };
		IntVar sumAS = new IntVar(store, "", 0, 4);
		IntVar sumBS = new IntVar(store, "", 0, 3);
		IntVar sumCS = new IntVar(store, "", 0, 2);
		store.impose(new Sum(aS, sumAS));
		store.impose(new Sum(bS, sumBS));
		store.impose(new Sum(cS, sumCS));
		store.impose(new IfThen(new XeqY(sumAS, new IntVar(store, "", 2, 2)),
				new XeqY(sumBS, new IntVar(store, "", 3, 3))));
		store.impose(new IfThen(new XeqY(sumAS, new IntVar(store, "", 2, 2)),
				new XneqY(sumAS, sumCS)));
		store.impose(new IfThen(new XeqY(sumCS, new IntVar(store, "", 2, 2)),
				new XneqY(sumCS, sumAS)));
//		 store.impose(new Not(new And(new XeqC(sumAS,2), new XeqC(sumCS,2))));
//		 alternative solution to the above two constraints
	}

	private static void workDaysAWeek(IntVar[] workerWeek, int i) {
		store.impose(new Sum(workerWeek, new IntVar(store, "", i, i)));
	}

}
